<!DOCTYPE html>
<title>Custom Elements: CustomElementRegistry.whenDefined</title>
<script src="../../../resources/testharness.js"></script>
<script src="../../../resources/testharnessreport.js"></script>
<script src="../resources/custom-elements-helpers.js"></script>
<body>
<div id="log"></div>
<script>
'use strict';
(() => {
  // https://html.spec.whatwg.org/multipage/scripting.html#dom-customelementregistry-whendefined
  // Use window from iframe to isolate the test.
  function setup() {
    const iframe = document.createElement('iframe');
    document.body.appendChild(iframe);
    const testWindow = iframe.contentWindow;
    const customElements = testWindow.customElements;
    if (!customElements)
      return Promise.reject('This test requires window.customElements');
    if (!('whenDefined' in customElements))
      return Promise.reject('This test requires window.customElements.whenDefined');
    return Promise.resolve(customElements);
  }

  const kNameToBeDefined = 'x-foo';
  const kNameNotDefined = 'x-bar';
  let beforeDefined = false;
  let notDefined = true;
  let afterDefined = false;
  promise_test(() => setup()
    .then(customElements => {
      // 4. If map does not contain an entry with key name, create an entry in
      // map with key name and whose value is a new promise.
      const promiseBeforeDefined = customElements.whenDefined(kNameToBeDefined);
      promiseBeforeDefined.then(() => beforeDefined = true);
      // 5. Let promise be the value of the entry in map with key name.
      // 6. Return promise
      assert_equals(promiseBeforeDefined,
          customElements.whenDefined(kNameToBeDefined),
          'There is only one promise defined before.');
      const promiseNotDefined = customElements.whenDefined(kNameNotDefined);
      promiseNotDefined.then(() => notDefined = false);
      assert_not_equals(promiseBeforeDefined, promiseNotDefined,
          'whenDefined() returns different promises for different names.');
      customElements.define(kNameToBeDefined, class {});
      // 3. If this CustomElementRegistry contains an entry with name name,
      // then return a new promise resolved with undefined and abort these
      // steps.
      const promiseAfterDefined = customElements.whenDefined(kNameToBeDefined);
      promiseAfterDefined.then(() => afterDefined = true);
      assert_not_equals(promiseBeforeDefined, promiseAfterDefined,
          'When name is defined, we should have a new promise.');
      assert_not_equals(promiseAfterDefined,
          customElements.whenDefined(kNameToBeDefined),
          'Once name is defined, whenDefined() always returns a new promise.');
      return customElements;
    }).then(customElements => {
      assert_true(beforeDefined, 'promise before defined should be resolved.');
      assert_true(afterDefined, 'promise after defined should be resolved.');
      assert_true(notDefined, 'promise for not defined name should not be resolved.');
    }).catch(reason => { throw reason }));

  // Calling whenDefined() with invalid names should throw "SyntaxError"DOMException
  // https://html.spec.whatwg.org/multipage/scripting.html#valid-custom-element-name
  let invalid_names = [
    'annotation-xml',
    'color-profile',
    'font-face',
    'font-face-src',
    'font-face-uri',
    'font-face-format',
    'font-face-name',
    'missing-glyph',
    'div', 'p',
    'nothtmlbutnohyphen',
    '-not-initial-a-z', '0not-initial-a-z', 'Not-initial-a-z',
    'intermediate-UPPERCASE-letters',
    'bad-\u00b6', 'bad-\u00b8', 'bad-\u00bf', 'bad-\u00d7', 'bad-\u00f7',
    'bad-\u037e', 'bad-\u037e', 'bad-\u2000', 'bad-\u200e', 'bad-\u203e',
    'bad-\u2041', 'bad-\u206f', 'bad-\u2190', 'bad-\u2bff', 'bad-\u2ff0',
    'bad-\u3000', 'bad-\ud800', 'bad-\uf8ff', 'bad-\ufdd0', 'bad-\ufdef',
    'bad-\ufffe', 'bad-\uffff', 'bad-' + String.fromCodePoint(0xf0000)
  ];

  invalid_names.forEach((name) => {
    promise_test((t) => {
      return create_window_in_test(t)
      .then((w) => {
        return promise_rejects_with_dom_exception_syntax_error(w, t, w.customElements.whenDefined(name));
      });
    }, 'whenDefined() called with invalid name ' + name + ' should throw "SyntaxError"DOMException');
  });

  function promise_rejects_with_dom_exception_syntax_error(global_context, test, promise, description) {
    return promise.then(test.unreached_func("Should have rejected: " + description)).catch(function(e) {
        assert_throws_dom('SYNTAX_ERR', global_context.DOMException, function () { throw e; })
      });
    }
})();
</script>
</body>
